Accelerators in Widget Applications
Keyboard accelerators allow the user to activate menu items or buttons using keyboard key combinations instead of the mouse.
Notes
- On Windows platforms, accelerators can be defined for menu items and other types of buttons.
- On UNIX and Mac, accelrators can only be defined for menu items.
- Context menu items do not support accelerators on any platform.
-
Menu item accelerators are only operational when the menu is closed.
- When an accelerator is implemented, it intercepts keyboard events before they are passed to the widgets. Widgets will never see keyboard events that are mapped to accelerators unless the accelerator is disabled as described in Disabling Button Widget Accelerators.
- Be mindful of the inherent operating system accelerators. For example, avoid Ctrl+Alt+Del on the Windows platform.
Specifying Accelerators
The ACCELERATOR keyword assigns a key combination that activates a menu item or button event. The value of the keyword is a case-insensitive string that specifies zero or more modifier keys (Ctrl, Shift, or Alt) and one other key. (For Mac users, special steps must be taken to enable Alt key accelerators—See the section below for details.)
When an accelerator is defined for a menu item, the ACCELERATOR keyword string is automatically displayed next to the menu item value. When an accelerator is defined for a button, the ACCELERATOR keyword string is not displayed. Therefore, for a button, we recommend that you include the accelerator shortcut within the button value or tooltip so that your users are aware of the option.
The following table gives the allowed accelerator key names, and also indicates whether that key requires using one of the Ctrl, Shift, or Alt modifiers:
Accelerator Key String |
Requires a Modifier |
---|---|
A–Z |
yes |
0–9 | yes |
Backspace, Space, Tab | yes |
Del or Delete, Down, End, Escape, Home, Insert, Left, Next or PageDown, Prior or PageUp, Return or Enter, Right, Up | no |
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24 |
no |
NumPadMultiply, NumPadAdd, NumPadSubtract, NumPadDecimal, NumPadDivide, NumPad0, NumPad1, NumPad2, NumPad3, NumPad4, NumPad5, NumPad6, NumPad7, NumPad8, NumPad9 |
yes |
Note: For the number pad accelerators the user must have the NumLock key activated.
Note: On Windows, Shift plus a number key will not work as an accelerator.
Enabling Alt Key Accelerators on Mac
Two steps are required to enable accelerators that use the Alt key to work with the Mac Command (open-Apple) key:
-
Create a
.Xmodmap
file in yourhome
folder and add the following three lines to it:clear mod1
clear mod2
add mod1 = Meta_L
When Apple’s X11 program starts, this file will automatically be read, and the Apple key will be mapped to the left Command key, which for IDL’s purposes is the Alt key. (Windows Alt key accelerators are mapped to the Mac Command key, not the Option (alt) key.)
- Run your system’s X11 program and change its preferences. Under Input in the X11 Preferences dialog, make sure that the following two items are unchecked:
- Follow system keyboard layout
- Enable key equivalents under X11
Note: You must relaunch the X11 program for these changes to take effect.
If you distribute your application to other Mac users, they too will need to have an appropriate .Xmodmap
and correct X11 Preferences settings in order for Alt key accelerators to work.
Example
The following simple example creates a variety of WIDGET_BUTTON types with accelerators.
; AcceleratorExample.pro
; Example of using keyboard accelerators.
pro acceleratorexample_event, event
WIDGET_CONTROL, event.ID, GET_VALUE = value
PRINT, 'Event on: ', value
IF ( uvalue EQ 'Quit' ) THEN BEGIN
WIDGET_CONTROL, event.TOP, /DESTROY
END
end
pro AcceleratorExample
tlb = WIDGET_BASE( /ROW, $
MBAR = mbar, TITLE = "Accelerator Example", $
XPAD = 10, YPAD = 10, XOFFSET = 25, YOFFSET = 25 )
; Create a menu with accelerators. The accelerator string is
; automatically displayed along with the menu item text.
file = WIDGET_BUTTON( mbar, /MENU, VALUE = "File" )
one = WIDGET_BUTTON( file, $
VALUE = "One", ACCELERATOR = "Ctrl+1" )
two = WIDGET_BUTTON( file, $
VALUE = "Two", ACCELERATOR = "Ctrl+2" )
three = WIDGET_BUTTON( file, $
VALUE = "Three", ACCELERATOR = "Ctrl+3" )
quit = WIDGET_BUTTON( file, $
VALUE = "Quit", ACCELERATOR = "Ctrl+Q" )
; Create a base with push buttons. Include the accelerator
; text in the button value so users are aware of it.
base = WIDGET_BASE( tlb, /COLUMN, /FRAME )
b1 = WIDGET_BUTTON( base, $
VALUE = "Affirmative (Ctrl+Y)", ACCELERATOR = "Ctrl+Y" )
b2 = WIDGET_BUTTON( base, $
VALUE = "Negative (Ctrl+N)", ACCELERATOR = "Ctrl+N" )
; Create a base with radio buttons.
base = WIDGET_BASE( tlb, /COLUMN, /FRAME, /EXCLUSIVE )
b1 = WIDGET_BUTTON( base, $
VALUE = "Owl (Ctrl+O)", ACCELERATOR = "Ctrl+O" )
b2 = WIDGET_BUTTON( base, $
VALUE = "Emu (Shift+E)", ACCELERATOR = "Shift+E" )
b3 = WIDGET_BUTTON( base, $
VALUE = "Bat (Alt+B)", ACCELERATOR = "Alt+B" )
; Create a base with check boxes.
base = WIDGET_BASE( tlb, /COLUMN, /FRAME, /NONEXCLUSIVE )
b1 = WIDGET_BUTTON( base, $
VALUE = "Hello (F3)", ACCELERATOR = "F3" )
b2 = WIDGET_BUTTON( base, $
VALUE = "Goodbye (F4)", ACCELERATOR = "F4" )
; Create the widgets and accept events.
WIDGET_CONTROL, tlb, /REALIZE
XMANAGER, 'acceleratorexample', tlb, /NO_BLOCK
IF !VERSION.OS_FAMILY NE 'Windows' THEN BEGIN
text = ['Accelerators on non-menu items are not supported', $
'on this platform.', ' ', $
'Accelerators on menu items work on all platforms.']
void = DIALOG_MESSAGE(text, /INFO)
ENDIF
END
Save and run the example. The Output Log window reports which button has been activated using the accelerator.
Disabling Button Widget Accelerators
Keyboard events are intercepted by the accelerators before they are passed along to widgets. This means that a widget will never see a keyboard event that maps to an accelerator. This can be resolved using one of the following methods:
- Use the IGNORE_ACCELERATORS keyword on those widgets that you want to receive keyboard input regardless of defined accelerators. This is the recommended method. See below for details.
- Disable the accelerated item by programmatically desensitizing the item when the widget that you want to receive events gains focus, and re-sensitize the item when the widget loses focus. This allows the keystrokes that would ordinarily map to the accelerator to reach the desired widget when it has focus.
Using IGNORE_ACCELERATORS
The IGNORE_ACCELERATORS keyword is available on the following widgets:
- WIDGET_COMBOBOX
- WIDGET_DRAW
- WIDGET_PROPERTYSHEET
- WIDGET_TABLE
- WIDGET_TEXT
For widgets with a text area, accelerator overrides are active only when focus is on an editable text portion. For the dra widget, accelerator overrides are active when the drawing area has focus. For example, when the focus is on a table cell that cannot be edited, accelerators are still enabled.
Set the IGNORE_ACCELERATORS equal to the text string of a single accelerator, an array containing multiple accelerator strings, or 1 (to ignore all accelerators).
Managing Accelerators Example
The following example shows various ways accelerators can be managed. This example creates several menu items with accelerators. Three text boxes either allow all accelerators, some accelerators or no accelerators to receive keyboard events. Additionally you can select a checkbox to desensitize the Delete menu item. When the menu item is desensitized, the accelerator never receives keyboard events.
PRO manage_accel_event, event
END
PRO quit_event, event
WIDGET_CONTROL, event.top, /DESTROY
END
PRO menu_event, event
PRINT, WIDGET_INFO( event.id, /UNAME )
END
PRO menu_sense_event, event
deleteItem = WIDGET_INFO( event.top, FIND_BY_UNAME ="MenuDel" )
WIDGET_CONTROL, deleteItem, SENSITIVE = event.select
END
pro manage_accel
; Create the top level base.
tlb = WIDGET_BASE( /COLUMN, MBAR = mbar, XSIZE = 250, /TAB_MODE)
; Build the menu bar.
edit= WIDGET_BUTTON( mbar, /MENU, VALUE = "Edit" )
menuDel = WIDGET_BUTTON( edit, VALUE = "Delete", $
UNAME = "MenuDel", ACCELERATOR = "Del", $
EVENT_PRO = "menu_event" )
menuCut = WIDGET_BUTTON( edit, VALUE = "Cut", $
UNAME = "MenuCut", ACCELERATOR = "Ctrl+X", $
EVENT_PRO = "menu_event" )
menuCopy = WIDGET_BUTTON( edit, VALUE = "Copy", $
UNAME = "MenuCopy", ACCELERATOR = "Ctrl+C", $
EVENT_PRO = "menu_event" )
menuPaste = WIDGET_BUTTON( edit, VALUE = "Paste", $
UNAME = "MenuPaste", ACCELERATOR = "Ctrl+V", $
EVENT_PRO = "menu_event" )
menuUndo = WIDGET_BUTTON( edit, VALUE = "Undo", $
UNAME = "MenuUndo", ACCELERATOR = "Ctrl+Z",$
EVENT_PRO = "menu_event" )
quit = WIDGET_BUTTON( edit, VALUE = "Quit", $
ACCELERATOR = "Ctrl+Q", EVENT_PRO = "quit_event" )
; Add text boxes with various levels of disabled accelerators.
text1 = WIDGET_TEXT( tlb, /EDITABLE, $
VALUE = "Doesn't use IGNORE_ACCELERATORS." )
text2 = WIDGET_TEXT( tlb, /EDITABLE, $
VALUE = "Receives Delete key and Ctrl+C combinations.", $
IGNORE_ACCELERATORS = [ "Del", "Ctrl+C" ] )
text3 = WIDGET_TEXT( tlb, /EDITABLE, $
VALUE = "Receives all accelerator key combinations.", $
IGNORE_ACCELERATORS = 1)
; Add a check box to desensitize the Delete menu item.
base2 = WIDGET_BASE( tlb, /FRAME, /NONEXCLUSIVE )
check1 = WIDGET_BUTTON( base2, VALUE = "Menu DEL sensitive", $
EVENT_PRO = "menu_sense_event" )
WIDGET_CONTROL, check1, SET_BUTTON = WIDGET_INFO ( menuDel, $
/SENSITIVE )
; Draw the widget.
WIDGET_CONTROL, tlb, /REALIZE
XMANAGER, "manage_accel_event", tlb, /NO_BLOCK
END
Compile and run the example. Try highlighting and deleting, or copying and pasting text in each textbox using accelerators defined in the Edit menu. All keyboard events are ineffective (stolen by the accelerators) when the first textbox has focus. The second textbox receives only copy and delete keyboard combinations. The third textbox receives all accelerators. When the delete menu item is desensitized, the Delete key can delete text from all textboxes. The IDL Output Log window prints the name of any menu item that is activated using an accelerator.